/*
 * Copyright 2010 - 2011, The PLDesktop Development Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#include "PLJavaInstructionParser.h"

#include <PLJava.h>
#include <filetypes/PLJavaFieldInfo.h>
#include <filetypes/PLJavaMethodeInfo.h>
#include <filetypes/PLJavaAttributeCode.h>
#include <filetypes/PLJavaStackValueInteger.h>
#include <filetypes/PLJavaConstClassInfo.h>
#include <filetypes/PLJavaConstRefInfo.h>
#include <filetypes/PLJavaConstNameAndType.h>
#include <filetypes/PLJavaStackValueClassRef.h>
#include <filetypes/PLJavaStackValueEmpty.h>
#include <filetypes/PLJavaDescriptorParser.h>
#include <filetypes/PLJavaStackValueClassArrayRef.h>
#include <filetypes/PLJavaStackValueArrayInteger.h>
#include <filetypes/PLJavaStackValueArrayBoolean.h>
#include <filetypes/PLJavaStackValueArrayChar.h>
#include <filetypes/PLJavaStackValueArrayFloat.h>
#include <filetypes/PLJavaStackValueArrayDouble.h>
#include <filetypes/PLJavaStackValueArrayByte.h>
#include <filetypes/PLJavaStackValueArrayShort.h>
#include <filetypes/PLJavaStackValueArrayLong.h>
#include <filetypes/PLJavaStackValueEmpty.h>
#include <filetypes/PLJavaStackValueFloat.h>
#include <filetypes/PLJavaStackValueDouble.h>
#include <filetypes/PLJavaStackValueLong.h>
#include <filetypes/PLJavaConstTypesFloat.h>
#include <filetypes/PLJavaConstTypesInteger.h>
#include <filetypes/PLJavaConstTypesDouble.h>
#include <filetypes/PLJavaConstTypesLong.h>

#include <stdio.h>
#include <string.h>

using namespace PLSDK;

PLJavaInstructionParser::PLJavaInstructionParser(u4 newCodeLength, uint8_t *newCode, PLJavaFile *fileData, PLJavaAttributeCode *newCodeData)
: m_codeLength(newCodeLength), m_code(newCode), m_fileData(fileData), m_codeData(newCodeData) {
}

void PLJavaInstructionParser::exec(PLJavaFrame *frame, PLJavaFrame *invoker) {
    u1 ins;
    for(u4 i = 0 ; i < m_codeLength ; i++) {
        ins = m_code[i];
//        printf("Inv %d ", ins);
        switch(ins) {
            // Do nothing
            // nop
            case 0x0:  //   0
                break;
            // Push null
            // aconst_null
            case 0x1:  //   1
                frame->stack()->push(new PLJavaStackValueEmpty());
                break;
            // Push int constant
            // iconst_m1
            case 0x2:   //  2
                frame->stack()->push(new PLJavaStackValueInteger(-1));
                break;
            // Push int constant
            // iconst_0
            case 0x3:   //  3
                frame->stack()->push(new PLJavaStackValueInteger(0));
                break;
            // Push int constant
            // iconst_1
            case 0x4:   //  4
                frame->stack()->push(new PLJavaStackValueInteger(1));
                break;
            // Push int constant
            // iconst_2
            case 0x5:   //  5
                frame->stack()->push(new PLJavaStackValueInteger(2));
                break;
            // Push int constant
            // iconst_3
            case 0x6:   //  6
                frame->stack()->push(new PLJavaStackValueInteger(3));
                break;
            // Push int constant
            // iconst_4
            case 0x7:   //  7
                frame->stack()->push(new PLJavaStackValueInteger(4));
                break;
            // Push int constant
            // iconst_5
            case 0x8:   //  8
                frame->stack()->push(new PLJavaStackValueInteger(5));
                break;
            // Push long constant
            // lconst_0
            case 0x9:   //  9
                frame->stack()->push(new PLJavaStackValueLong(0));
                break;
            // Push long constant
            // lconst_1
            case 0xa:   //  10
                frame->stack()->push(new PLJavaStackValueLong(1));
                break;
            // Push float
            // fconst_0
            case 0xb:   //  11
                frame->stack()->push(new PLJavaStackValueFloat(0.0, false));
                break;
            // Push float
            // fconst_1
            case 0xc:   //  12
                frame->stack()->push(new PLJavaStackValueFloat(1.0, false));
                break;
            // Push float
            // fconst_2
            case 0xd:   //  13
                frame->stack()->push(new PLJavaStackValueFloat(2.0, false));
                break;
            // Push double
            // dconst_0
            case 0xe:  //  14
                frame->stack()->push(new PLJavaStackValueDouble(0.0, false, false, false));
                break;
            // Push double
            // dconst_1
            case 0xf:  //  15
                frame->stack()->push(new PLJavaStackValueDouble(1.0, false, false, false));
                break;
            // Push byte
            // bipush
            case 0x10:  //  16
                pushByte(&i, frame);
                break;
            // Push item from runtime constant pool
            // ldc
            case 0x12:  //  18
                ldc(&i, frame);
                break;
            // Push long or double from runtime constant pool (wide index)
            // ldc2_w
            case 0x14:  //  20
                ldc2_w(&i, frame);
                break;
            // Load reference from local variable
            // aload
            case 0x19:  //  25
                aload(&i, frame);
                break;
            // Load int from local variable
            // iload_0
            case 0x1a:  //  26
                loadInt0(frame, 0);
                break;
            // Load int from local variable
            // iload_1
            case 0x1b:  //  27
                loadInt0(frame, 1);
                break;
            // Load int from local variable
            // iload_2
            case 0x1c:  //  28
                loadInt0(frame, 2);
                break;
            // Load int from local variable
            // iload_3
            case 0x1d:  //  29
                loadInt0(frame, 3);
                break;
            // Load long from local variable
            // lload_0
            case 0x1e:  //  30
                lload0(frame, 0);
                break;
            // Load long from local variable
            // lload_1
            case 0x1f:  //  31
                lload0(frame, 1);
                break;
            // Load long from local variable
            // lload_2
            case 0x20:  //  32
                lload0(frame, 2);
                break;
            // Load long from local variable
            // lload_3
            case 0x21:  //  33
                lload0(frame, 3);
                break;
            // Load float from local variable
            // fload_0
            case 0x22:  //  34
                fload0(frame, 0);
                break;
            // Load float from local variable
            // fload_1
            case 0x23:  //  35
                fload0(frame, 1);
                break;
            // Load float from local variable
            // fload_2
            case 0x24:  //  36
                fload0(frame, 2);
                break;
            // Load float from local variable
            // fload_3
            case 0x25:  //  37
                fload0(frame, 3);
                break;
            // Load double from local variable
            // dload_0
            case 0x26:  //  38
                dload0(frame, 0);
                break;
            // Load double from local variable
            // dload_1
            case 0x27:  //  39
                dload0(frame, 1);
                break;
            // Load double from local variable
            // dload_2
            case 0x28:  //  40
                dload0(frame, 2);
                break;
            // Load double from local variable
            // dload_3
            case 0x29:  //  41
                dload0(frame, 3);
                break;
            // Get a local variable reference
            // aload_0
            case 0x2a:  //  42
                aload0(frame, 0);
                break;
            // Get a local variable reference
            // aload_1
            case 0x2b:  //  43
                aload0(frame, 1);
                break;
            // Get a local variable reference
            // aload_2
            case 0x2c:  //  44
                aload0(frame, 2);
                break;
            // Get a local variable reference
            // aload_3
            case 0x2d:  //  45
                aload0(frame, 3);
                break;
            // Load int from array
            // iaload
            case 0x2e:  //  46
                iaload(frame);
                break;
            // Load long from array
            // laload
            case 0x2f:  //  47
                laload(frame);
                break;
            // Load float from array
            // faload
            case 0x30:  //  48
                faload(frame);
                break;
            // Load double from array
            // daload
            case 0x31:  //  49
                daload(frame);
                break;
            // Load reference from array
            // aaload
            case 0x32:  //  50
                aaload(frame);
                break;
            // Load byte or boolean from array
            // baload
            case 0x33:  //  51
                baload(frame);
                break;
            // Load char from array
            // caload
            case 0x34:  //  52
                caload(frame);
                break;
            // Load short from array
            // saload
            case 0x35:  //  53
                saload(frame);
                break;
            // Store reference into local variable
            // astore
            case 0x3a:  //  58
                astore(&i, frame);
                break;
            // Store int into local variable.
            // istore_0
            case 0x3b:  //  59
                storeInt0(frame, 0);
                break;
            // Store int into local variable.
            // istore_1
            case 0x3c:  //  60
                storeInt0(frame, 1);
                break;
            // Store int into local variable.
            // istore_2
            case 0x3d:  //  61
                storeInt0(frame, 2);
                break;
            // Store int into local variable.
            // istore_3
            case 0x3e:  //  62
                storeInt0(frame, 3);
                break;
            // Store long into local variable
            // lstore_0
            case 0x3f:  //  63
                lstore0(frame, 0);
                break;
            // Store long into local variable
            // lstore_1
            case 0x40:  //  64
                lstore0(frame, 1);
                break;
            // Store long into local variable
            // lstore_1
            case 0x41:  //  65
                lstore0(frame, 2);
                break;
            // Store long into local variable
            // lstore_2
            case 0x42:  //  66
                lstore0(frame, 3);
                break;
            // Store float into local variable
            // fstore_0
            case 0x43:  //  67
                storeFloat0(frame, 0);
                break;
            // Store float into local variable
            // fstore_1
            case 0x44:  //  68
                storeFloat0(frame, 1);
                break;
            // Store float into local variable
            // fstore_2
            case 0x45:  //  69
                storeFloat0(frame, 2);
                break;
            // Store float into local variable
            // fstore_3
            case 0x46:  //  70
                storeFloat0(frame, 3);
                break;
            // Store double into local variable
            // dstore_0
            case 0x47:  //  71
                dstore0(frame, 0);
                break;
            // Store double into local variable
            // dstore_1
            case 0x48:  //  72
                dstore0(frame, 1);
                break;
            // Store double into local variable
            // dstore_2
            case 0x49:  //  73
                dstore0(frame, 2);
                break;
            // Store double into local variable
            // dstore_3
            case 0x4a:  //  74
                dstore0(frame, 3);
                break;
            // Store reference into local variable
            // astore_0
            case 0x4b:  //  75
                storeRef0(frame, 0);
                break;
            // Store reference into local variable
            // astore_1
            case 0x4c:  //  76
                storeRef0(frame, 1);
                break;
            // Store reference into local variable
            // astore_2
            case 0x4d:  //  77
                storeRef0(frame, 2);
                break;
            // Store reference into local variable
            // astore_3
            case 0x4e:  //  78
                storeRef0(frame, 3);
                break;
            // Store into int array
            // iastore
            case 0x4f:  //  79
                iastore(frame);
                break;
            // Store into long array
            // lastore
            case 0x50:  //  80
                lastore(frame);
                break;
            // Store into float array
            // fastore
            case 0x51:  //  81
                fastore(frame);
                break;
            // Store into double array
            // dastore
            case 0x52:  //  82
                dastore(frame);
                break;
            // Store into reference array
            // aastore
            case 0x53:  //  83
                aastore(frame);
                break;
            // Store into byte or boolean array
            // bastore
            case 0x54:  //  84
                bastore(frame);
                break;
            // Store into char array
            // castore
            case 0x55:  //  85
                castore(frame);
                break;
            // Store into short array
            // sastore
            case 0x56:
                sastore(frame);
                break;
            // Pop the top operand stack value
            // pop
            case 0x57:  //  87
                pop(frame);
                break;
            // Duplicate the top operand stack value
            // dup
            case 0x59:  //  89
                duplicateTheTop(frame);
                break;
            // Add int
            // iadd
            case 0x60:
                iadd(frame);
                break;
            // Add long
            // ladd
            case 0x61:
                ladd(frame);
                break;
            // Add float
            // fadd
            case 0x62:  //  98
                fadd(frame);
                break;
            // Add double
            // dadd
            case 0x63:  //  99
                dadd(frame);
                break;
            // Convert int to long
            // i2l
            case 0x85:  // 133
                i2l(frame);
                break;
            // Convert int to double
            // i2d
            case 0x87:  // 135
                i2d(frame);
                break;
            // Convert double to int
            // d2i
            case 0x8e:  // 142
                d2i(frame);
                break;
            // Convert double to long
            // d2l
            case 0x8f:  // 143
                d2l(frame);
                break;
            // Convert double to float
            // d2f
            case 0x90:  // 144
                d2f(frame);
                break;
            // Convert int to byte
            // i2b
            case 0x91:  // 145
                i2b(frame);
                break;
            // Convert int to short
            // i2s
            case 0x93:  // 147
                i2s(frame);
                break;
            // Compare double
            // dcmpl
            case 0x97:  // 151
                dcmp(frame, true);
                break;
            // Compare double
            // dcmpg
            case 0x98:  // 152
                dcmp(frame, false);
                break;
            // Branch if int comparison with zero succeeds
            // ifeq
            case 0x99:  // 153
                ifeq(&i, frame);
                break;
            // Branch if int comparison with zero succeeds
            // ifne
            case 0x9a:  // 154
                ifne(&i, frame);
                break;
            // Branch if int comparison with zero succeeds
            // iflt
            case 0x9b:  // 155
                iflt(&i, frame);
                break;
            // Branch if int comparison with zero succeeds
            // ifge
            case 0x9c:  // 156
                ifge(&i, frame);
                break;
            // Branch if int comparison with zero succeeds
            // ifgt
            case 0x9d:  // 157
                ifgt(&i, frame);
                break;
            // Branch if int comparison with zero succeeds
            // ifle
            case 0x9e:  // 158
                ifle(&i, frame);
                break;
            // Branch always
            // goto
            case 0xa7:  // 167
                gotoF(&i);
                break;
            // Return int from method
            // ireturn
            case 0xac:  // 172
                ireturn(frame, invoker);
                break;
            // Return long from method
            // lreturn
            case 0xad:
                lreturn(frame, invoker);
                break;
            // Return float from method
            // freturn
            case 0xae:  // 174
                freturn(frame, invoker);
                break;
            // Return double from method
            // dreturn
            case 0xaf:  // 175
                dreturn(frame, invoker);
                break;
            // Return reference from method
            // areturn
            case 0xb0:  // 176
                areturn(frame, invoker);
                break;
            // Return void from method
            case 0xb1:  // 177
                returnVoid(frame);
                break;
            // Fetch field from object
            // getfield
            case 0xb4:  // 180
                getfield(&i, frame);
                break;
            // Set field in object
            // putfield
            case 0xb5:  // 181
                putfield(&i, frame);
                break;
            // Invoke instance method; dispatch based on class
            // invokevirtual
            case 0xb6:  // 182
                invokeVirtualInstanceMethod(&i, frame);
                break;
            // Invoke instance methode
            case 0xb7:  // 183
                invokeInstanceMethode(&i, frame);
                break;
            // Create new array
            // newarray
            case 0xbc:  // 188
                newarray(&i, frame);
                break;
            // Create new array of reference
            // anewarray
            case 0xbd:  // 189
                anewarray(&i, frame);
                break;
            // Get length of array
            // arraylength
            case 0xbe:  // 190
                arraylength(frame);
                break;
            // Throw exception or error
            // athrow
            case 0xbf:  // 191
                athrow(frame);
                break;
            // Create new object
            // new
            case 0xbb:  // 187
                createNewObject(&i, frame);
                break;
            // Check whether object is of given type
            // checkcast
            case 0xc0:  // 192
                checkcast(frame);
                break;
            default:
                printf("Error instruction code %d (0x%x) was not implemented.", ins, ins);
                break;
        }
    }
}

PLJavaFile *PLJavaInstructionParser::createFileIfNotExists(const PLJavaConstTypesUtf8 *className) {
    PLJavaFile *javaFile = PLJava::instance().javaFile(className->string());
    if(!javaFile) {
        PLJava::instance().addJavaFile(className->string());
        javaFile = PLJava::instance().javaFile(className->string());
    }
    return javaFile;
}

void PLJavaInstructionParser::pop(PLJavaFrame *frame) {
    PLJavaStackValue *val = frame->stack()->pop();
    delete val;
}

void PLJavaInstructionParser::pushByte(u4 *i, PLJavaFrame *frame) {
    u4 v = (u4)m_code[++(*i)];
    frame->stack()->push(new PLJavaStackValueInteger(v));
}

void PLJavaInstructionParser::invokeInstanceMethode(u4 *i, PLJavaFrame *frame) {
    u2 index = (u2)m_code[++(*i)] << 8;
    index += (u2)m_code[++(*i)];
    const PLJavaConst *constEntry = m_fileData->constTableEntry(index - 1);
    const PLJavaConstRefInfo *methodeRef = (const PLJavaConstRefInfo *)constEntry;
    const PLJavaConstClassInfo *classInfo = (const PLJavaConstClassInfo *)m_fileData->constTableEntry(methodeRef->classIndex() - 1);
    const PLJavaConstTypesUtf8 *className = m_fileData->nameAtIndex(classInfo->nameIndex());
    const PLJavaConstNameAndType *nameAndType = (const PLJavaConstNameAndType *)m_fileData->constTableEntry(methodeRef->nameAndTypeIndex() - 1);
    const PLJavaConstTypesUtf8 *methodeName = m_fileData->nameAtIndex(nameAndType->nameIndex());
    const PLJavaConstTypesUtf8 *descriptor = (PLJavaConstTypesUtf8 *)m_fileData->constTableEntry(nameAndType->descriptorIndex() - 1);
    PLJavaDescriptorParser descriptorParse(descriptor);
    PLJavaFile *javaFile = createFileIfNotExists(className);
    PLJavaMethodeInfo *methode = javaFile->methode(methodeName->string());
    if(m_fileData->accessFlags().testFlag(PLJavaFile::AccessFlagSuper) && methode) {
        if(strcmp(methode->name()->string(), "<init>") == 0 || strcmp(methode->name()->string(), "<clinit>") == 0) {
            if(methode->code()) {
                PLJavaFrame *_frame = new PLJavaFrame(methode->code()->maxStack(), methode->code()->maxLocals());
                for(int j = descriptorParse.paramterCount() - 1 ; j >= 0 ; j--) {
                    _frame->localVariables()->set(j + 1, frame->stack()->pop());
                }
                _frame->localVariables()->set(0, frame->stack()->pop());
                methode->code()->codeParser()->exec(_frame, frame);
                delete frame;
            }
        } else {
            printf("Error the invoked methode was %s.", methode->name()->string());
        }
    }
}

void PLJavaInstructionParser::createNewObject(u4 *i, PLJavaFrame *frame) {
    u2 index = (u2)m_code[++(*i)] << 8;
    index += (u2)m_code[++(*i)];
    const PLJavaConst *constEntry = m_fileData->constTableEntry(index - 1);
    if(constEntry->type() == PLJavaConst::ConstTypesClass) {
        const PLJavaConstClassInfo *classInfo = (const PLJavaConstClassInfo *)constEntry;
        const PLJavaConstTypesUtf8 *className = m_fileData->nameAtIndex(classInfo->nameIndex());
        PLJavaFile *javaFile = createFileIfNotExists(className);
        frame->stack()->push(new PLJavaStackValueClassRef(javaFile));
    }
}

void PLJavaInstructionParser::aload0(PLJavaFrame *frame, int pos) {
    PLJavaStackValue *c = frame->localVariables()->get(pos);
    if(c) {
        frame->stack()->push(c);
    } else {
        frame->stack()->push(new PLJavaStackValueEmpty());
    }
}

void PLJavaInstructionParser::loadInt0(PLJavaFrame *frame, int pos) {
    PLJavaStackValue *c = frame->localVariables()->get(pos);
    if(c && c->type() == PLJavaStackValue::Integer) {
        frame->stack()->push(c);
    } else {
        frame->stack()->push(new PLJavaStackValueEmpty());
    }
}

void PLJavaInstructionParser::storeInt0(PLJavaFrame *frame, int pos) {
    PLJavaStackValue *c = frame->stack()->pop();
    if(c->type() == PLJavaStackValue::Integer) {
        frame->localVariables()->set(pos, c);
    } else {
        printf("Error stack type mismatch in instruction istore_%d.", pos);
    }
}

void PLJavaInstructionParser::storeRef0(PLJavaFrame *frame, int pos) {
    PLJavaStackValue *c = frame->stack()->pop();
    if(c->type() == PLJavaStackValue::ClassRef || c->type() == PLJavaStackValue::ClassArrayRef || c->type() == PLJavaStackValue::ArrayRef || c->type() == PLJavaStackValue::Double) {
        frame->localVariables()->set(pos, c);
    } else {
        printf("Error stack type mismatch in instruction astore_%d the type was %d.", pos, c->type());
    }
}

void PLJavaInstructionParser::duplicateTheTop(PLJavaFrame *frame) {
    PLJavaStackValue *c = frame->stack()->pop();
    frame->stack()->push(c);
    frame->stack()->push(c);
}

void PLJavaInstructionParser::returnVoid(PLJavaFrame *frame) {
    PLJavaStackValue *val;
    while(!frame->stack()->isEmpty()) {
        val = frame->stack()->pop();
        delete val;
    }
}

void PLJavaInstructionParser::iadd(PLJavaFrame *frame) {
    PLJavaStackValue *c2 = frame->stack()->pop();
    if(c2->type() == PLJavaStackValue::Integer) {
        PLJavaStackValue *c1 = frame->stack()->pop();
        if(c1->type() == PLJavaStackValue::Integer) {
            PLJavaStackValueInteger *val2 = (PLJavaStackValueInteger *)c2;
            PLJavaStackValueInteger *val1 = (PLJavaStackValueInteger *)c1;
            frame->stack()->push(new PLJavaStackValueInteger(val1->value() + val2->value()));
            delete c1;
            delete c2;
        } else {
            printf("Error stack type mismatch in instruction iadd in first value.");
        }
    } else {
        printf("Error stack type mismatch in instruction iadd in second value.");
    }
}

void PLJavaInstructionParser::invokeVirtualInstanceMethod(u4 *i, PLJavaFrame *frame) {
    u2 index = (u2)m_code[++(*i)] << 8;
    index += (u2)m_code[++(*i)];
    const PLJavaConst *constEntry = m_fileData->constTableEntry(index - 1);
    const PLJavaConstRefInfo *methodeRef = (const PLJavaConstRefInfo *)constEntry;
    const PLJavaConstClassInfo *classInfo = (const PLJavaConstClassInfo *)m_fileData->constTableEntry(methodeRef->classIndex() - 1);
    const PLJavaConstTypesUtf8 *className = m_fileData->nameAtIndex(classInfo->nameIndex());
    const PLJavaConstNameAndType *nameAndType = (const PLJavaConstNameAndType *)m_fileData->constTableEntry(methodeRef->nameAndTypeIndex() - 1);
    const PLJavaConstTypesUtf8 *methodeName = m_fileData->nameAtIndex(nameAndType->nameIndex());
    const PLJavaConstTypesUtf8 *descriptor = (PLJavaConstTypesUtf8 *)m_fileData->constTableEntry(nameAndType->descriptorIndex() - 1);
    PLJavaDescriptorParser descriptorParse(descriptor);
    PLJavaFile *javaFile = createFileIfNotExists(className);
    PLJavaMethodeInfo *methode = javaFile->methode(methodeName->string());
    if(methode) {
        if(methode->code()) {
            PLJavaFrame *_frame = new PLJavaFrame(methode->code()->maxStack(), methode->code()->maxLocals());
            for(int j = descriptorParse.paramterCount() - 1 ; j >= 0 ; j--) {
                _frame->localVariables()->set(j + 1, frame->stack()->pop());
            }
            _frame->localVariables()->set(0, frame->stack()->pop());
            methode->code()->codeParser()->exec(_frame, frame);
            delete frame;
        }
    }
}

void PLJavaInstructionParser::anewarray(u4 *i, PLJavaFrame *frame) {
    PLJavaStackValue *countValue = frame->stack()->pop();
    if(countValue->type() != PLJavaStackValue::Integer) {
        printf("Ane array type mismatch. The count must be an int value.");
        return;
    }
    int count = ((PLJavaStackValueInteger *)countValue)->value();
    u2 arrayref = (u2)m_code[++(*i)] << 8;
    arrayref += (u2)m_code[++(*i)];
    const PLJavaConst *constEntry = m_fileData->constTableEntry(arrayref - 1);
    if(constEntry->type() == PLJavaConst::ConstTypesClass) {
//        PLJavaConstClassInfo *entryClass = (PLJavaConstClassInfo *)constEntry;
//        PLJavaConstTypesUtf8 *className = m_fileData->nameAtIndex(entryClass->nameIndex());
//        PLJavaFile *javaFile = createFileIfNotExists(className);
        frame->stack()->push(new PLJavaStackValueClassArrayRef(count));
    } else {
        printf("Array entry type mismatch in anewarray.");
        return;
    }
}

void PLJavaInstructionParser::ireturn(PLJavaFrame *frame, PLJavaFrame *invoker) {
    PLJavaStackValue *c = frame->stack()->pop();
    if(c->type() == PLJavaStackValue::Integer) {
        invoker->stack()->push(c);
    } else {
        printf("Error the return type was not a integer value.");
    }
}

void PLJavaInstructionParser::aastore(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ClassArrayRef) {
        ((PLJavaStackValueClassArrayRef *)array)->setClassRefAt(((PLJavaStackValueInteger *)index)->value(), (PLJavaStackValueClassRef *)value);
    } else {
        printf("Type mismatch in aastore. The poped values have not the right type.");
    }
}

void PLJavaInstructionParser::aaload(PLJavaFrame *frame) {
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ClassArrayRef) {
        PLJavaStackValue *value = ((PLJavaStackValueClassArrayRef *)array)->classRefAt(((PLJavaStackValueInteger *)index)->value());
        frame->stack()->push(value);
    } else {
        printf("Type mismatch in aaload. The poped values have not the right type.");
    }
}

void PLJavaInstructionParser::newarray(u4 *i, PLJavaFrame *frame) {
    u1 atype = m_code[++(*i)];
    PLJavaStackValue *count = frame->stack()->pop();
    if(count->type() == PLJavaStackValue::Integer) {
        PLJavaStackValueInteger *countType = (PLJavaStackValueInteger *)count;
        switch(atype) {
            case PLJavaStackValueArray::Boolean:
                frame->stack()->push(new PLJavaStackValueArrayBoolean(countType->value()));
                break;
            case PLJavaStackValueArray::Char:
                frame->stack()->push(new PLJavaStackValueArrayChar(countType->value()));
                break;
            case PLJavaStackValueArray::Float:
                frame->stack()->push(new PLJavaStackValueArrayFloat(countType->value()));
                break;
            case PLJavaStackValueArray::Double:
                frame->stack()->push(new PLJavaStackValueArrayDouble(countType->value()));
                break;
            case PLJavaStackValueArray::Byte:
                frame->stack()->push(new PLJavaStackValueArrayByte(countType->value()));
                break;
            case PLJavaStackValueArray::Short:
                frame->stack()->push(new PLJavaStackValueArrayShort(countType->value()));
                break;
            case PLJavaStackValueArray::Integer:
                frame->stack()->push(new PLJavaStackValueArrayInteger(countType->value()));
                break;
            case PLJavaStackValueArray::Long:
                frame->stack()->push(new PLJavaStackValueArrayLong(countType->value()));
                break;
            default:
                printf("Type from the array was not implemented.");
                return;
        }
        delete countType;
    } else {
        printf("Type mismatch in newarray. The count type must be a integer type.");
    }
}

void PLJavaInstructionParser::iastore(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer && index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Integer) {
            PLJavaStackValueArrayInteger *arrayInt = (PLJavaStackValueArrayInteger *)arrayValue;
            arrayInt->setAt(((PLJavaStackValueInteger *)index)->value(), (PLJavaStackValueInteger *)value);
        } else {
            printf("Array type, type mismatch in iastore.");
        }
    } else {
        printf("Type mismatch for the values from iastore.");
    }
    delete index;
}

void PLJavaInstructionParser::bastore(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer && index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Boolean) {
            PLJavaStackValueArrayBoolean *arrayBool = (PLJavaStackValueArrayBoolean *)arrayValue;
            arrayBool->setAt(((PLJavaStackValueInteger *)index)->value(), (PLJavaStackValueInteger *)value);
        } else {
            printf("Array type, type mismatch in bastore.");
        }
    } else if(value->type() == PLJavaStackValue::Integer && index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ClassArrayRef) {
        PLJavaStackValueClassArrayRef *arrayValue = (PLJavaStackValueClassArrayRef *)array;
        arrayValue->setClassRefAt(((PLJavaStackValueInteger *)index)->value(), value);
    } else {
        printf("Type mismatch for the values from bastore.");
    }
    delete index;
}

void PLJavaInstructionParser::baload(PLJavaFrame *frame) {
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Boolean) {
            PLJavaStackValueArrayBoolean *arrayBool = (PLJavaStackValueArrayBoolean *)arrayValue;
            PLJavaStackValueInteger *value = arrayBool->at(((PLJavaStackValueInteger *)index)->value());
            frame->stack()->push(value);
        } else {
            printf("Array type, type mismatch in baload.");
        }
    } else if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ClassArrayRef) {
        PLJavaStackValueClassArrayRef *arrayValue = (PLJavaStackValueClassArrayRef *)array;
        PLJavaStackValue *val = arrayValue->classRefAt(((PLJavaStackValueInteger *)index)->value());
        if(val->type() == PLJavaStackValue::Integer) {
            frame->stack()->push(val);
        } else {
            printf("Class Array Ref type mismatch in baload.");
        }
    } else {
        printf("Type mismatch for the values from baload.");
    }
    delete index;
}

void PLJavaInstructionParser::iaload(PLJavaFrame *frame) {
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Integer) {
            PLJavaStackValueArrayInteger *arrayInt = (PLJavaStackValueArrayInteger *)arrayValue;
            PLJavaStackValueInteger *value = arrayInt->at(((PLJavaStackValueInteger *)index)->value());
            frame->stack()->push(value);
        } else {
            printf("Array type, type mismatch in iaload.");
        }
    } else {
        printf("Type mismatch for the values from baload.");
    }
    delete index;
}

void PLJavaInstructionParser::castore(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer && index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Char) {
            PLJavaStackValueArrayChar *arrayInt = (PLJavaStackValueArrayChar *)arrayValue;
            arrayInt->setAt(((PLJavaStackValueInteger *)index)->value(), (PLJavaStackValueInteger *)value);
        } else {
            printf("Array type, type mismatch in castore.");
        }
    } else {
        printf("Type mismatch for the values from castore.");
    }
    delete index;
}

void PLJavaInstructionParser::caload(PLJavaFrame *frame) {
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Char) {
            PLJavaStackValueArrayChar *arrayInt = (PLJavaStackValueArrayChar *)arrayValue;
            PLJavaStackValueInteger *value = arrayInt->at(((PLJavaStackValueInteger *)index)->value());
            frame->stack()->push(value);
        } else {
            printf("Array type, type mismatch in caload.");
        }
    } else {
        printf("Type mismatch for the values from caload.");
    }
    delete index;
}

void PLJavaInstructionParser::ldc(u4 *i, PLJavaFrame *frame) {
    u1 index = m_code[++(*i)];
    const PLJavaConst *constEntry = m_fileData->constTableEntry(index - 1);
    if(constEntry->type() == PLJavaConst::ConstTypesFloat) {
        PLJavaConstTypesFloat *val = (PLJavaConstTypesFloat *)constEntry;
        frame->stack()->push(new PLJavaStackValueFloat(val->number(), val->nan()));
    } else if(constEntry->type() == PLJavaConst::ConstTypesInteger) {
        PLJavaConstTypesInteger *val = (PLJavaConstTypesInteger *)constEntry;
        frame->stack()->push(new PLJavaStackValueInteger(val->integer()));
    } else if(constEntry->type() == PLJavaConst::ConstTypesString) {
        printf("TODO implement string ldc.");
    } else {
        printf("Type mismatch for the values from ldc.");
    }
}

void PLJavaInstructionParser::storeFloat0(PLJavaFrame *frame, int pos) {
    PLJavaStackValue *c = frame->stack()->pop();
    if(c->type() == PLJavaStackValue::Float) {
        frame->localVariables()->set(pos, c);
    } else {
        printf("Error stack type mismatch in instruction fstore_%d.", pos);
    }
}

void PLJavaInstructionParser::fload0(PLJavaFrame *frame, int pos) {
    PLJavaStackValue *c = frame->localVariables()->get(pos);
    if(c && c->type() == PLJavaStackValue::Float) {
        frame->stack()->push(c);
    } else {
        frame->stack()->push(new PLJavaStackValueEmpty());
    }
}

void PLJavaInstructionParser::fadd(PLJavaFrame *frame) {
    PLJavaStackValue *c2 = frame->stack()->pop();
    if(c2->type() == PLJavaStackValue::Float) {
        PLJavaStackValue *c1 = frame->stack()->pop();
        if(c1->type() == PLJavaStackValue::Float) {
            PLJavaStackValueFloat *val2 = (PLJavaStackValueFloat *)c2;
            PLJavaStackValueFloat *val1 = (PLJavaStackValueFloat *)c1;
            // TODO Test for nan.
            bool nan = false;
            frame->stack()->push(new PLJavaStackValueFloat(val1->number() + val2->number(), nan));
            delete c1;
            delete c2;
        } else {
            printf("Error stack type mismatch in instruction iadd in first value.");
        }
    } else {
        printf("Error stack type mismatch in instruction iadd in second value.");
    }
}

void PLJavaInstructionParser::freturn(PLJavaFrame *frame, PLJavaFrame *invoker) {
    PLJavaStackValue *c = frame->stack()->pop();
    if(c->type() == PLJavaStackValue::Float) {
        invoker->stack()->push(c);
    } else {
        printf("Error the return type was not a float value.");
    }
}

void PLJavaInstructionParser::fastore(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Float && index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Float) {
            PLJavaStackValueArrayFloat *arrayInt = (PLJavaStackValueArrayFloat *)arrayValue;
            arrayInt->setAt(((PLJavaStackValueInteger *)index)->value(), (PLJavaStackValueFloat *)value);
        } else {
            printf("Array type, type mismatch in fastore.");
        }
    } else {
        printf("Type mismatch for the values from fastore.");
    }
    delete index;
}

void PLJavaInstructionParser::faload(PLJavaFrame *frame) {
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Float) {
            PLJavaStackValueArrayFloat *arrayFloat = (PLJavaStackValueArrayFloat *)arrayValue;
            PLJavaStackValueFloat *value = arrayFloat->at(((PLJavaStackValueFloat *)index)->number());
            frame->stack()->push(value);
        } else {
            printf("Array type, type mismatch in faload.");
        }
    } else {
        printf("Type mismatch for the values from faload.");
    }
    delete index;
}

void PLJavaInstructionParser::ldc2_w(u4 *i, PLJavaFrame *frame) {
    u2 index = (u2)m_code[++(*i)] << 8;
    index += (u2)m_code[++(*i)];
    const PLJavaConst *constEntry = m_fileData->constTableEntry(index - 1);
    if(constEntry->type() == PLJavaConst::ConstTypesDouble) {
        PLJavaConstTypesDouble *val = (PLJavaConstTypesDouble *)constEntry;
        frame->stack()->push(new PLJavaStackValueDouble(val->doubleValue(), val->nan(), val->negInfinity(), val->posInfinity()));
    } else if(constEntry->type() == PLJavaConst::ConstTypesLong) {
        PLJavaConstTypesLong *val = (PLJavaConstTypesLong *)constEntry;
        frame->stack()->push(new PLJavaStackValueLong(val->longValue()));
    } else {
        printf("Type mismatch for the values from ldc.");
    }
}

void PLJavaInstructionParser::dstore0(PLJavaFrame *frame, int pos) {
    PLJavaStackValue *c = frame->stack()->pop();
    if(c->type() == PLJavaStackValue::Double) {
        frame->localVariables()->set(pos, c);
    } else {
        printf("Error stack type mismatch in instruction istore_%d.", pos);
    }
}

void PLJavaInstructionParser::astore(u4 *i, PLJavaFrame *frame) {
    u1 index = m_code[++(*i)];
    PLJavaStackValue *ref = frame->stack()->pop();
    if(ref->type() == PLJavaStackValue::Double) {
        frame->localVariables()->set(index, ref);
    } else if(ref->type() == PLJavaStackValue::Long) {
        frame->localVariables()->set(index, ref);
    } else if(ref->type() == PLJavaStackValue::ClassRef) {
        frame->localVariables()->set(index, ref);
    } else {
        printf("Error stack type mismatch in astore. The type was %d.", ref->type());
    }
}

void PLJavaInstructionParser::aload(u4 *i, PLJavaFrame *frame) {
    u1 index = m_code[++(*i)];
    PLJavaStackValue *c = frame->localVariables()->get(index);
    if(c) {
        frame->stack()->push(c);
    } else {
        frame->stack()->push(new PLJavaStackValueEmpty());
    }
}

void PLJavaInstructionParser::dload0(PLJavaFrame *frame, int pos) {
    PLJavaStackValue *c = frame->localVariables()->get(pos);
    if(c && c->type() == PLJavaStackValue::Double) {
        frame->stack()->push(c);
    } else {
        frame->stack()->push(new PLJavaStackValueEmpty());
    }
}

void PLJavaInstructionParser::dadd(PLJavaFrame *frame) {
    PLJavaStackValue *c2 = frame->stack()->pop();
    if(c2->type() == PLJavaStackValue::Double) {
        PLJavaStackValue *c1 = frame->stack()->pop();
        if(c1->type() == PLJavaStackValue::Double) {
            PLJavaStackValueDouble *val2 = (PLJavaStackValueDouble *)c2;
            PLJavaStackValueDouble *val1 = (PLJavaStackValueDouble *)c1;
            // TODO add overflow testing.
            frame->stack()->push(new PLJavaStackValueDouble(val1->number() + val2->number(), false, false, false));
            delete c1;
            delete c2;
        } else {
            printf("Error stack type mismatch in instruction dadd in first value.");
        }
    } else {
        printf("Error stack type mismatch in instruction dadd in second value.");
    }
}

void PLJavaInstructionParser::dreturn(PLJavaFrame *frame, PLJavaFrame *invoker) {
    PLJavaStackValue *c = frame->stack()->pop();
    if(c->type() == PLJavaStackValue::Double) {
        invoker->stack()->push(c);
    } else {
        printf("Error the return type was not a double value.");
    }
}

void PLJavaInstructionParser::dastore(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Double && index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Double) {
            PLJavaStackValueArrayDouble *arrayDouble = (PLJavaStackValueArrayDouble *)arrayValue;
            arrayDouble->setAt(((PLJavaStackValueInteger *)index)->value(), (PLJavaStackValueDouble *)value);
        } else {
            printf("Array type, type mismatch in dastore.");
        }
    } else if(value->type() == PLJavaStackValue::Double && index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ClassArrayRef) {
        PLJavaStackValueClassArrayRef *arrayValue = (PLJavaStackValueClassArrayRef *)array;
        arrayValue->setClassRefAt(((PLJavaStackValueInteger *)index)->value(), value);
    } else {
        printf("Type mismatch for the values from dastore.");
    }
    delete index;
}

void PLJavaInstructionParser::daload(PLJavaFrame *frame) {
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Double) {
            PLJavaStackValueArrayDouble *arrayDouble = (PLJavaStackValueArrayDouble *)arrayValue;
            PLJavaStackValueDouble *value = arrayDouble->at(((PLJavaStackValueInteger *)index)->value());
            frame->stack()->push(value);
        } else {
            printf("Array type, type mismatch in daload.");
        }
    } else if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ClassArrayRef) {
        PLJavaStackValueClassArrayRef *arrayValue = (PLJavaStackValueClassArrayRef *)array;
        PLJavaStackValue *value = arrayValue->classRefAt(((PLJavaStackValueInteger *)index)->value());
        frame->stack()->push(value);
    } else {
        printf("Type mismatch for the values from daload.");
    }
    delete index;
}

void PLJavaInstructionParser::i2b(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer) {
        PLJavaStackValueInteger *val = (PLJavaStackValueInteger *)value;
        val->setValue(val->value() > 255 ? 255 : val->value() < 0 ? 0 : val->value());
        frame->stack()->push(val);
    } else {
        printf("Type mismatch for the value from i2b.");
    }
}

void PLJavaInstructionParser::i2s(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer) {
        PLJavaStackValueInteger *val = (PLJavaStackValueInteger *)value;
        val->setValue(val->value() > 32767 ? 32767 : val->value() < -32768 ? -32768 : val->value());
        frame->stack()->push(val);
    } else {
        printf("Type mismatch for the value from i2s.");
    }
}

void PLJavaInstructionParser::sastore(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer && index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Short) {
            PLJavaStackValueArrayShort *arrayShort = (PLJavaStackValueArrayShort *)arrayValue;
            arrayShort->setAt(((PLJavaStackValueInteger *)index)->value(), (PLJavaStackValueInteger *)value);
        } else {
            printf("Array type, type mismatch in sastore.");
        }
    } else if(value->type() == PLJavaStackValue::Integer && index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ClassArrayRef) {
        PLJavaStackValueClassArrayRef *arrayValue = (PLJavaStackValueClassArrayRef *)array;
        arrayValue->setClassRefAt(((PLJavaStackValueInteger *)index)->value(), value);
    } else {
        printf("Type mismatch for the values from sastore.");
    }
    delete index;
}

void PLJavaInstructionParser::saload(PLJavaFrame *frame) {
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Short) {
            PLJavaStackValueArrayShort *arrayInt = (PLJavaStackValueArrayShort *)arrayValue;
            PLJavaStackValueInteger *value = arrayInt->at(((PLJavaStackValueInteger *)index)->value());
            frame->stack()->push(value);
        } else {
            printf("Array type, type mismatch in saload.");
        }
    } else if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ClassArrayRef) {
        PLJavaStackValueClassArrayRef *arrayValue = (PLJavaStackValueClassArrayRef *)array;
        PLJavaStackValue *value = arrayValue->classRefAt(((PLJavaStackValueInteger *)index)->value());
        frame->stack()->push(value);
    } else {
        printf("Type mismatch for the values from saload.");
    }
    delete index;
}

void PLJavaInstructionParser::lstore0(PLJavaFrame *frame, int pos) {
    PLJavaStackValue *c = frame->stack()->pop();
    if(c->type() == PLJavaStackValue::Long) {
        frame->localVariables()->set(pos, c);
    } else {
        printf("Error stack type mismatch in instruction lstore_%d.", pos);
    }
}

void PLJavaInstructionParser::lload0(PLJavaFrame *frame, int pos) {
    PLJavaStackValue *c = frame->localVariables()->get(pos);
    if(c && c->type() == PLJavaStackValue::Long) {
        frame->stack()->push(c);
    } else {
        frame->stack()->push(new PLJavaStackValueEmpty());
    }
}

void PLJavaInstructionParser::ladd(PLJavaFrame *frame) {
    PLJavaStackValue *c2 = frame->stack()->pop();
    if(c2->type() == PLJavaStackValue::Long) {
        PLJavaStackValue *c1 = frame->stack()->pop();
        if(c1->type() == PLJavaStackValue::Long) {
            PLJavaStackValueLong *val2 = (PLJavaStackValueLong *)c2;
            PLJavaStackValueLong *val1 = (PLJavaStackValueLong *)c1;
            frame->stack()->push(new PLJavaStackValueLong(val1->value() + val2->value()));
            delete c1;
            delete c2;
        } else {
            printf("Error stack type mismatch in instruction ladd in first value.");
        }
    } else {
        printf("Error stack type mismatch in instruction ladd in second value.");
    }
}

void PLJavaInstructionParser::lreturn(PLJavaFrame *frame, PLJavaFrame *invoker) {
    PLJavaStackValue *c = frame->stack()->pop();
    if(c->type() == PLJavaStackValue::Long) {
        invoker->stack()->push(c);
    } else {
        printf("Error the return type was not a long value.");
    }
}

void PLJavaInstructionParser::lastore(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Long && index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Long) {
            PLJavaStackValueArrayLong *arrayLong = (PLJavaStackValueArrayLong *)arrayValue;
            arrayLong->setAt(((PLJavaStackValueInteger *)index)->value(), (PLJavaStackValueLong *)value);
        } else {
            printf("Array type, type mismatch in lastore.");
        }
    } else if(value->type() == PLJavaStackValue::Long && index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ClassArrayRef) {
        PLJavaStackValueClassArrayRef *arrayValue = (PLJavaStackValueClassArrayRef *)array;
        arrayValue->setClassRefAt(((PLJavaStackValueInteger *)index)->value(), value);
    } else {
        printf("Type mismatch for the values from lastore.");
    }
    delete index;
}

void PLJavaInstructionParser::laload(PLJavaFrame *frame) {
    PLJavaStackValue *index = frame->stack()->pop();
    PLJavaStackValue *array = frame->stack()->pop();
    if(index->type() == PLJavaStackValue::Integer && array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        if(arrayValue->arrayType() == PLJavaStackValueArray::Long) {
            PLJavaStackValueArrayLong *arrayLong = (PLJavaStackValueArrayLong *)arrayValue;
            PLJavaStackValueLong *value = arrayLong->at(((PLJavaStackValueInteger *)index)->value());
            frame->stack()->push(value);
        } else {
            printf("Array type, type mismatch in laload.");
        }
    } else {
        printf("Type mismatch for the values from laload.");
    }
    delete index;
}

void PLJavaInstructionParser::areturn(PLJavaFrame *frame, PLJavaFrame *invoker) {
    PLJavaStackValue *c = frame->stack()->pop();
    invoker->stack()->push(c);
}

void PLJavaInstructionParser::arraylength(PLJavaFrame *frame) {
    PLJavaStackValue *array = frame->stack()->pop();
    if(array->type() == PLJavaStackValue::ArrayRef) {
        PLJavaStackValueArray *arrayValue = (PLJavaStackValueArray *)array;
        frame->stack()->push(new PLJavaStackValueInteger(arrayValue->count()));
    } else {
        printf("Type mismatch in arraylength.");
    }
}

void PLJavaInstructionParser::i2l(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer) {
        PLJavaStackValueInteger *val = (PLJavaStackValueInteger *)value;
        frame->stack()->push(new PLJavaStackValueLong((long)val->value()));
    } else {
        printf("Type mismatch for the value from i2l.");
    }
}

void PLJavaInstructionParser::d2f(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Double) {
        PLJavaStackValueDouble *val = (PLJavaStackValueDouble *)value;
        frame->stack()->push(new PLJavaStackValueFloat((float)val->number(), val->nan()));
    } else {
        printf("Type mismatch for the value from d2f.");
    }
}

void PLJavaInstructionParser::d2i(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Double) {
        PLJavaStackValueDouble *val = (PLJavaStackValueDouble *)value;
        frame->stack()->push(new PLJavaStackValueInteger((int)val->number()));
    } else {
        printf("Type mismatch for the value from d2i.");
    }
}

void PLJavaInstructionParser::d2l(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Double) {
        PLJavaStackValueDouble *val = (PLJavaStackValueDouble *)value;
        frame->stack()->push(new PLJavaStackValueLong((long)val->number()));
    } else {
        printf("Type mismatch for the value from d2l.");
    }
}

void PLJavaInstructionParser::i2d(PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer) {
        PLJavaStackValueInteger *val = (PLJavaStackValueInteger *)value;
        frame->stack()->push(new PLJavaStackValueDouble((double)val->value(), false, false, false));
    } else {
        printf("Type mismatch for the value from i2d.");
    }
}

void PLJavaInstructionParser::dcmp(PLJavaFrame *frame, bool dcmpl) {
    PLUNUSED(dcmpl)
    PLJavaStackValue *value1 = frame->stack()->pop();
    PLJavaStackValue *value2 = frame->stack()->pop();
    if(value1->type() == PLJavaStackValue::Double && value2->type() == PLJavaStackValue::Double) {
        PLJavaStackValueDouble *val1 = (PLJavaStackValueDouble *)value1;
        PLJavaStackValueDouble *val2 = (PLJavaStackValueDouble *)value2;
        if(val1->number() > val2->number()) {
            frame->stack()->push(new PLJavaStackValueInteger(1));
        } else if(val1->number() < val2->number()) {
            frame->stack()->push(new PLJavaStackValueInteger(-1));
        } else {
            frame->stack()->push(new PLJavaStackValueInteger(0));
        }
    }
}

void PLJavaInstructionParser::athrow(PLJavaFrame *frame) {
    PLUNUSED(frame)
    printf("TODO implement athrow.");
}

void PLJavaInstructionParser::checkcast(PLJavaFrame *frame) {
    PLUNUSED(frame)
    printf("TODO implement checkcast.");
}

void PLJavaInstructionParser::ifeq(u4 *i, PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer) {
        int index = (int)m_code[++(*i)] << 8;
        index += (int)m_code[++(*i)];
        PLJavaStackValueInteger *val = (PLJavaStackValueInteger *)value;
        if(val->value() == 0) {
            *i += index;
        }
    } else {
        printf("Type mismatch in ifeq.");
    }
}

void PLJavaInstructionParser::ifne(u4 *i, PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer) {
        int index = (int)m_code[++(*i)] << 8;
        index += (int)m_code[++(*i)];
        PLJavaStackValueInteger *val = (PLJavaStackValueInteger *)value;
        if(val->value() != 0) {
            *i += index;
        }
    } else {
        printf("Type mismatch in ifeq.");
    }
}

void PLJavaInstructionParser::iflt(u4 *i, PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer) {
        int index = (int)m_code[++(*i)] << 8;
        index += (int)m_code[++(*i)];
        PLJavaStackValueInteger *val = (PLJavaStackValueInteger *)value;
        if(val->value() < 0) {
            *i += index;
        }
    } else {
        printf("Type mismatch in ifeq.");
    }
}

void PLJavaInstructionParser::ifge(u4 *i, PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer) {
        int index = (int)m_code[++(*i)] << 8;
        index += (int)m_code[++(*i)];
        PLJavaStackValueInteger *val = (PLJavaStackValueInteger *)value;
        if(val->value() >= 0) {
            *i += index;
        }
    } else {
        printf("Type mismatch in ifeq.");
    }
}

void PLJavaInstructionParser::ifgt(u4 *i, PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer) {
        int index = (int)m_code[++(*i)] << 8;
        index += (int)m_code[++(*i)];
        PLJavaStackValueInteger *val = (PLJavaStackValueInteger *)value;
        if(val->value() > 0) {
            *i += index;
        }
    } else {
        printf("Type mismatch in ifeq.");
    }
}

void PLJavaInstructionParser::ifle(u4 *i, PLJavaFrame *frame) {
    PLJavaStackValue *value = frame->stack()->pop();
    if(value->type() == PLJavaStackValue::Integer) {
        int index = (int)m_code[++(*i)] << 8;
        index += (int)m_code[++(*i)];
        PLJavaStackValueInteger *val = (PLJavaStackValueInteger *)value;
        if(val->value() <= 0) {
            *i += index;
        }
    } else {
        printf("Type mismatch in ifeq.");
    }
}

void PLJavaInstructionParser::gotoF(u4 *i) {
    int index = (int)m_code[++(*i)] << 8;
    index += (int)m_code[++(*i)];
    *i += index;
}

void PLJavaInstructionParser::putfield(u4 *i, PLJavaFrame *frame) {
    int index = (int)m_code[++(*i)] << 8;
    index += (int)m_code[++(*i)];
    PLJavaStackValue *value = frame->stack()->pop();
    PLJavaStackValue *ref = frame->stack()->pop();
    if(ref->type() != PLJavaStackValue::ClassRef) {
        printf("The reference type in putfield was not a reference.");
        return;
    }
    PLJavaConstRefInfo *refInfo = (PLJavaConstRefInfo *)m_fileData->constTableEntry(index - 1);
    const PLJavaConstNameAndType *nameAndType = (const PLJavaConstNameAndType *)(m_fileData->constTableEntry(refInfo->nameAndTypeIndex() - 1));
    PLJavaDescriptorParser desc((PLJavaConstTypesUtf8 *)(m_fileData->constTableEntry(nameAndType->descriptorIndex() - 1)));
    PLJavaStackValueClassRef *reference = (PLJavaStackValueClassRef *)ref;
    switch(desc.returnValue()->type()) {
        case PLJavaDescriptorValue::Byte:
        case PLJavaDescriptorValue::Char:
        case PLJavaDescriptorValue::Boolean:
        case PLJavaDescriptorValue::Int:
        case PLJavaDescriptorValue::Short:
            if(value->type() == PLJavaStackValue::Integer) {
                reference->setField((PLJavaConstTypesUtf8 *)(m_fileData->constTableEntry(nameAndType->nameIndex())), value);
            }
            break;
        case PLJavaDescriptorValue::Long:
            if(value->type() == PLJavaStackValue::Long) {
                reference->setField((PLJavaConstTypesUtf8 *)(m_fileData->constTableEntry(nameAndType->nameIndex())), value);
            }
            break;
        case PLJavaDescriptorValue::Double:
            if(value->type() == PLJavaStackValue::Double) {
                reference->setField((PLJavaConstTypesUtf8 *)(m_fileData->constTableEntry(nameAndType->nameIndex())), value);
            }
            break;
        case PLJavaDescriptorValue::Float:
            if(value->type() == PLJavaStackValue::Float) {
                reference->setField((PLJavaConstTypesUtf8 *)(m_fileData->constTableEntry(nameAndType->nameIndex())), value);
            }
            break;
        default:
            printf("Error the type mismatch in putfield.");
            return;
            //Reference
            //Array
    }
}

void PLJavaInstructionParser::getfield(u4 *i, PLJavaFrame *frame) {
    int index = (int)m_code[++(*i)] << 8;
    index += (int)m_code[++(*i)];
    PLJavaStackValue *ref = frame->stack()->pop();
    if(ref->type() == PLJavaStackValue::ClassRef) {
        PLJavaConstRefInfo *refInfo = (PLJavaConstRefInfo *)m_fileData->constTableEntry(index - 1);
        const PLJavaConstNameAndType *nameAndType = (const PLJavaConstNameAndType *)(m_fileData->constTableEntry(refInfo->nameAndTypeIndex() - 1));
        PLJavaDescriptorParser desc((PLJavaConstTypesUtf8 *)(m_fileData->constTableEntry(nameAndType->descriptorIndex() - 1)));
        PLJavaStackValueClassRef *reference = (PLJavaStackValueClassRef *)ref;
        switch(desc.returnValue()->type()) {
            case PLJavaDescriptorValue::Byte:
            case PLJavaDescriptorValue::Char:
            case PLJavaDescriptorValue::Boolean:
            case PLJavaDescriptorValue::Int:
            case PLJavaDescriptorValue::Short:
            {
                PLJavaStackValue *value = reference->field((PLJavaConstTypesUtf8 *)(m_fileData->constTableEntry(nameAndType->nameIndex())));
                if(value->type() == PLJavaStackValue::Integer) {
                    frame->stack()->push(value);
                } else {
                    printf("Error type mismatch from field value with the correct type for integer.");
                }
                break;
            }
            case PLJavaDescriptorValue::Long:
            {
                PLJavaStackValue *value = reference->field((PLJavaConstTypesUtf8 *)(m_fileData->constTableEntry(nameAndType->nameIndex())));
                if(value->type() == PLJavaStackValue::Long) {
                    frame->stack()->push(value);
                } else {
                    printf("Error type mismatch from field value with the correct type for long.");
                }
                break;
            }
            case PLJavaDescriptorValue::Double:
            {
                PLJavaStackValue *value = reference->field((PLJavaConstTypesUtf8 *)(m_fileData->constTableEntry(nameAndType->nameIndex())));
                if(value->type() == PLJavaStackValue::Double) {
                    frame->stack()->push(value);
                } else {
                    printf("Error type mismatch from field value with the correct type for double.");
                }
                break;
            }
            case PLJavaDescriptorValue::Float:
            {
                PLJavaStackValue *value = reference->field((PLJavaConstTypesUtf8 *)(m_fileData->constTableEntry(nameAndType->nameIndex())));
                if(value->type() == PLJavaStackValue::Float) {
                    frame->stack()->push(value);
                } else {
                    printf("Error type mismatch from field value with the correct type for float.");
                }
                break;
            }
            default:
                printf("Error the type mismatch in getfield.");
                return;
        }
    } else {
        printf("The reference type in getfield was not a reference.");
    }
}
